const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
function resolvePromise(promise2, x, resolve, reject) {// 主要解决then返回promise的问题
    /**
     * x 上一个then的返回值 
     *  promise2 当前then返回的promise
     * resolve reject  上一个then的resolve reject
     * 
     * */

    if (x === promise2) { // then返回上一个promise 则status一直不会切换
        reject(new TypeError('重复调用promise2'))
    }
    if ((typeof x === 'object' && x !== null) || (typeof x === 'function')) { // 非基本类型
        let called = false
        try {
            let then = x.then
            if (typeof then === 'function') {
                then.call(x, (y) => {
                    // value 可能为promise
                    if (called) return
                    called = true;
                    resolvePromise(promise2, y, resolve, reject)
                }, (r) => {
                    if (called) return
                    called = true;
                    reject(r)
                })
            } else {
                resolve(x)
            }


        } catch (error) {
            if (called) return
            called = true;
            reject(error)
        }

    } else {  // 基本类型
        resolve(x)
    }
}

class Promise {
    constructor(executor) {
        this.status = PENDING
        this.value = undefined  // 成功值
        this.reason = undefined  // 失败原因
        this.onResolveCallback = []  // 记录多个then的 成功  多个then调用
        this.onRejectedCallback = [] // 记录多个then的 失败
        const resolve = (value) => {
            if (value instanceof Promise) {
                return value.then(resolve, reject)
            }
            if (this.status === PENDING) {
                this.value = value
                this.status = FULFILLED
                this.onResolveCallback.forEach(cb => cb(this.value))
            }

        }
        const reject = (reason) => {
            if (this.status === PENDING) {
                this.reason = reason
                this.status = REJECTED
                this.onRejectedCallback.forEach(cb => cb(this.reason))
            }

        }
        try {
            executor(resolve, reject)
        } catch (error) {
            reject(error)
        }


    }

    then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v  // 多个then返回值穿透
        onRejected = typeof onRejected === 'function' ? onRejected : v => { throw v }
        let promise2 = new Promise((resolve, reject) => {

            if (this.status === FULFILLED) {
                setTimeout(() => {
                    // 通过异步 在promise2调用promise
                    // 为了确保上一个promise已经执行完成 
                    try {
                        let x = onFulfilled(this.value)

                        resolvePromise(promise2, x, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                })

            }
            if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        let x = onRejected(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                })
            }
            if (this.status === PENDING) {
                this.onResolveCallback.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onFulfilled(this.value)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    })
                })
                this.onRejectedCallback.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onRejected(this.reason)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    })
                }
                )
            }
        })
        return promise2
    }
    catch(err) {
        this.then(null, err)
    }
    static resolve(value) {
        return new Promise(resolve => {
            resolve(value)
        })
    }
    static reject(reason) {
        return new Promise((_, reject) => {
            reject(reason)
        })
    }
    static all(processList) {
        return new Promise((resolve, reject) => {
            let result = [], index = 0
            //  用index判段的原因： then中调用addList是异步的i ==3可能比1先调用 result[3]时 length已经成了3
            function addList(i, value) {
                result[i] = value
                if (++index === processList.length) {
                    return resolve(result)
                }
            }
            for (let i = 0; i < processList.length; i++) {
                let value = processList[i]
                if (value && typeof value.then === 'function') {
                    value.then((res) => {
                        addList(i, res)
                    }, reject)
                } else {
                    addList(i, value)
                }
            }

        })

    }
    finally(cb) {
        return this.then((y) => {
            return Promise.resolve(cb()).then(d => y)
        }, (r) => {
            return Promise.resolve(cb()).then(d => { throw d })
        })
    }
    static race(promises) {
        return new Promise((resolve, reject) => {
            for (let promise of promises) {
                if (typeof promise === 'object' && typeof promise.then === 'function') {
                    promise.then((r) => {
                        resolve(r)
                    }, (r) => {
                        reject(r)
                    })
                } else {
                    resolve(promise)
                }
            }
        })

    }
}
// 原生不支持此方法
Promise.deferred = function () {
    let dfd = {};
    dfd.promise = new Promise((resolve, reject) => {
        dfd.resolve = resolve;
        dfd.reject = reject;
    });
    return dfd
}


module.exports = Promise